package ru.nstu.isma.core.lang;

import ru.nstu.isma.core.hsm.*;
import ru.nstu.isma.core.hsm.exp.*;
import ru.nstu.isma.core.hsm.pde.HMPDEBound;
import ru.nstu.isma.core.hsm.pde.HMPartialDerEquation;

/**
 * Created with IntelliJ IDEA.
 * User: abv
 * Date: 05.12.13
 * Time: 13:28
 * To change this template use File | Settings | File Templates.
 */
public class HSM2TextTranslator {
    public static String convert(HMModel model) {
        StringBuilder sb = new StringBuilder();
        printVariableTable("GLOBAL", model.getVariables(), sb);
        for(String s : model.getAutomata().getStates().keySet()) {
            HMState ss = model.getAutomata().getState(s);
            printState(ss, sb);
        }
        sb.append("=== Transactions ========================\n" );
        for (HMTransaction ts : model.getAutomata().getTransactions()) {
            sb.append("from ").append(ts.getSource().getCode());
            sb.append(" to ").append(ts.getTarget().getCode());
            sb.append(" when ");
            printExpression(ts.getCondition(), sb);
            sb.append("\n");
        }

        return sb.toString();
    }

    private static StringBuilder printVariableTable(String name, HMVariableTable vt,  StringBuilder sb) {
        sb.append("=== Variable table dump: " ).append(name).append("========================\n" );

//        for (String vk : vt.keySet()) {
//            HMVariable vv = vt.get(vk);
//            printVariable(vv, sb);
//            sb.append("\n");
//        }

        printUnnamedConsts(vt, sb);
        printConsts(vt, sb);

        printEquations(vt, sb);

        return sb;
    }

    private static StringBuilder printState(HMState st,  StringBuilder sb) {
        printVariableTable(st.getCode(), st.getVariables(), sb);
        return sb;
    }


    private static StringBuilder printConsts(HMVariableTable vt,  StringBuilder sb) {
        sb.append("// --- Constants: --------------------\n" );

        for (String vk : vt.keySet()) {
            HMVariable vv = vt.get(vk);
            if (vv instanceof HMConst && !(vv instanceof HMUnnamedConst)) {
                printConst((HMConst) vv, sb);
                sb.append("\n");
            }
        }
        sb.append("\n");
        return sb;
    }

    private static StringBuilder printUnnamedConsts(HMVariableTable vt,  StringBuilder sb) {
        sb.append("// --- Unnamed constants: -------------------- \n" );

        for (String vk : vt.keySet()) {
            HMVariable vv = vt.get(vk);
            if (vv instanceof HMUnnamedConst) {
                printUConst((HMUnnamedConst) vv, sb);
                sb.append("\n");
            }

        }
        sb.append("\n");
        return sb;
    }

    private static StringBuilder printEquations(HMVariableTable vt,  StringBuilder sb) {
        sb.append("// --- Equations: -------------------- \n" );

        for (String vk : vt.keySet()) {
            HMVariable vv = vt.get(vk);
            if (vv instanceof HMAlgebraicEquation) {
                printAlgebraicEquation((HMAlgebraicEquation) vv, sb);
                sb.append("\n");
            } else if (vv instanceof HMPartialDerEquation) {
                printPartialDerEquation((HMPartialDerEquation) vv, sb);
                sb.append("\n");
            } else if (vv instanceof HMDerivativeEquation) {
                printDerivativeEquation((HMDerivativeEquation) vv, sb);
                sb.append("\n");
            }
        }
        sb.append("\n");
        return sb;
    }

    private static StringBuilder printVariable(HMVariable v, StringBuilder sb) {
        if (v instanceof HMUnnamedConst) {
            printUConst((HMUnnamedConst) v, sb);
        } else if (v instanceof HMConst) {
            printConst((HMConst) v, sb);
        } else if (v instanceof HMAlgebraicEquation) {
            printAlgebraicEquation((HMAlgebraicEquation) v, sb);
        } else if (v instanceof HMDerivativeEquation) {
            printDerivativeEquation((HMDerivativeEquation) v, sb);
        } else if (v instanceof HMPartialDerEquation) {
            printPartialDerEquation((HMPartialDerEquation) v, sb);
        }
        return sb;
    }

    private static StringBuilder printConst(HMConst c, StringBuilder sb) {
        return sb.append("[const] ").append(c.getCode()).append(" = ").append(c.getValue());
    }

    private static StringBuilder printUConst(HMUnnamedConst c, StringBuilder sb) {
        return sb.append("[unnamed_const] ").append(c.getValue()).append(" // \"").append(c.getCode()).append("\"");
    }

    private static StringBuilder printAlgebraicEquation(HMAlgebraicEquation eq, StringBuilder sb) {
        sb.append("[equation] ");
        sb.append(eq.getCode());
        sb.append(" = ");
        printExpression(eq.getRightPart(), sb);
        return sb;
    }

    private static StringBuilder printDerivativeEquation(HMDerivativeEquation eq, StringBuilder sb) {
        sb.append("[ode] ").append(eq.getCode()).append(" = ");
        printExpression(eq.getRightPart(), sb);
        sb.append("   // initial = ");
        printExpression(eq.getInitial().getRightPart(), sb);
        return sb;
    }

    private static StringBuilder printPartialDerEquation(HMPartialDerEquation eq, StringBuilder sb) {
        sb.append("[pde] ").append(eq.getCode()).append(" = ");
        printExpression(eq.getRightPart(), sb);
        sb.append("   //  initial = ");
        printExpression(eq.getInitial().getRightPart(), sb);

        for(HMPDEBound b : eq.getBoundaries()) {
            sb.append("; edge ");
            sb.append(b.getEquation().getCode()).append(" = ");
            HMConst val = b.getValue();
            if (val instanceof HMUnnamedConst) {
                sb.append(val.getValue());
            } else {
                sb.append(val.getCode());
            }
            sb.append(" at ").append(b.getAtApxVar().getCode()).append(" ").append(b.getSide().toString());
        }

        return sb;
    }

    private static StringBuilder printExpression(HMExpression exp, StringBuilder sb) {
        if (exp == null ){
            sb.append("null");
            return sb;
        }
        exp.toString(sb, false);
        return sb;
    }


}
